package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

    @Autowired
    private AlbumInfoMapper albumInfoMapper;
    @Autowired
    private AlbumAttributeValueMapper albumAttributeValueMapper;
    @Autowired
    private AlbumStatMapper albumStatMapper;
    @Autowired
    private TrackInfoMapper trackInfoMapper;
    @Autowired
    private KafkaService kafkaService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 新增专辑
     * TODO必须登录才能新增专辑
     *
     * @return
     */
    @Transactional
    public void saveAlbumInfo(AlbumInfoVo albumInfoVo, Long userId) {
        //1.获取微信用户id
        //2.保存专辑信息
        //2.1转化AlbumInfoVo为AlbumInfo
        AlbumInfo albumInfo = BeanUtil.copyProperties(albumInfoVo, AlbumInfo.class);
        albumInfo.setUserId(userId);
        //2.2如果不是免费类型，设置免费试听集数和免费试听秒数
        if (!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(albumInfo.getPayType())) {
            albumInfo.setTracksForFree(5);
        }
        //目前没有平台审核端暂时写为通过
        albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
        albumInfoMapper.insert(albumInfo);
        Long albumInfoId = albumInfo.getId();
        //3.专辑属性关联表
        List<AlbumAttributeValue> volist = albumInfo.getAlbumAttributeValueVoList();
        if (CollectionUtil.isNotEmpty(volist)) {
            for (AlbumAttributeValue albumAttributeValue : volist) {
                albumAttributeValue.setAlbumId(albumInfoId);
                albumAttributeValueMapper.insert(albumAttributeValue);
            }
        }
        //4.向专辑统计表中新增四条记录(播放数，订阅数，购买数，评论数)
        this.saveAlbumStat(albumInfoId, SystemConstant.ALBUM_STAT_PLAY);
        this.saveAlbumStat(albumInfoId, SystemConstant.ALBUM_STAT_SUBSCRIBE);
        this.saveAlbumStat(albumInfoId, SystemConstant.ALBUM_STAT_BROWSE);
        this.saveAlbumStat(albumInfoId, SystemConstant.ALBUM_STAT_COMMENT);

        //同时将新上架状态公开的专辑存入ES 发送上架消息
        if ("1".equals(albumInfo.getIsOpen())) {
            kafkaService.sendMessage(KafkaConstant.QUEUE_ALBUM_UPPER, String.valueOf(albumInfo.getId()));
        }
    }

    /**
     * 初始化保存专辑统计信息
     *
     * @param albumId  专辑ID
     * @param statType 统计类型
     */
    @Override
    public void saveAlbumStat(Long albumId, String statType) {
        AlbumStat albumStat = new AlbumStat();
        albumStat.setAlbumId(albumId);
        albumStat.setStatType(statType);
        albumStat.setStatNum(0);
        albumStatMapper.insert(albumStat);
    }

    /**
     * 查询用户专辑列表
     *
     * @param pageinfo 分页信息
     * @param query    查询条件
     * @return
     */
    @Override
    public Page<AlbumListVo> getUserAlbumPage(Page<AlbumListVo> pageInfo, AlbumInfoQuery albumInfoQuery) {
        //1.分页查询info表
        //2.转为AlbumListVo
        //3.查询统计表为AlbumListVo赋值
        return albumInfoMapper.getUserAlbumByPage(pageInfo, albumInfoQuery);
    }

    /**
     * 删除专辑
     * 0.查询用户是否是专辑创建者
     * 1.删除专辑信息
     * 2.删除专辑属性关联表
     * 3.删除专辑统计表
     * 4.删除专辑音频表
     *
     * @param id 专辑ID
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeAlbumInfo(Long id) {
        Long userId = AuthContextHolder.getUserId();
        Long albumUserId = albumInfoMapper.selectById(id).getUserId();
        if (!userId.equals(albumUserId)) {
            throw new RuntimeException("您没有删除专辑的权限");
        }
        //1.删除专辑信息
        albumInfoMapper.deleteById(id);
        //2.删除专辑属性关联表
        albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>()
                .eq(AlbumAttributeValue::getAlbumId, id));
        //3.删除专辑统计表
        albumStatMapper.delete(new LambdaQueryWrapper<AlbumStat>()
                .eq(AlbumStat::getAlbumId, id));
        //4.删除专辑音频表
        //4.根据专辑ID删除声音列表
        trackInfoMapper.delete(new LambdaQueryWrapper<TrackInfo>()
                .eq(TrackInfo::getAlbumId, id));
        //TODO 同步删除索引库中专辑文档
        kafkaService.sendMessage(KafkaConstant.QUEUE_ALBUM_LOWER, id.toString());
    }


    /**
     * 先采用SpringDataRedis分布式锁 解决缓存击穿
     * 根据ID查询专辑信息
     *
     * @param id
     * @return
     */
//    @Override
//    public AlbumInfo getAlbumInfo(Long id) {
//        try {
//            String datakey = RedisConstant.ALBUM_INFO_PREFIX + id;
//            String lockKey = RedisConstant.ALBUM_LOCK_SUFFIX + id;
//            String lockValue = IdUtil.fastSimpleUUID();
//            //1.1从缓存获取数据
//            AlbumInfo albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(datakey);
//            //1.2如果缓存中有数据，直接返回
//            if (albumInfo != null){
//                return albumInfo;
//            }
//            //2.如果缓存中没有数据，分布式锁,从数据库查询，然后放入缓存中，返回数据
//            //2.1 获取锁
//            Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, RedisConstant.ALBUM_LOCK_EXPIRE_PX2, TimeUnit.SECONDS);
//            //2.2 获得锁成功
//            if (flag){
//                try {
//                    //3. 查询数据库
//                    albumInfo = this.getAlbumInfoFromDB(id);
//                    //3.1 如果没有数据,设置较短缓存过期时间，返回空数据
//                    if(albumInfo == null){
//                        redisTemplate.opsForValue().set(datakey,null, RedisConstant.ALBUM_TEMPORARY_TIMEOUT, TimeUnit.SECONDS);
//                        return albumInfo;
//                    }
//                    //3.2 如果有数据，设置缓存过期时间，返回数据
//                    long ttl = RandomUtil.randomLong(500, 3600);
//                    redisTemplate.opsForValue().set(datakey,albumInfo, RedisConstant.ALBUM_TIMEOUT+ttl, TimeUnit.SECONDS);
//                    return albumInfo;
//                } finally {
//                //4. 释放锁,使用rua脚本
//                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
//    //            //4.1设置脚本文本
//                String text = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
//                        "then\n" +
//                        "    return redis.call(\"del\",KEYS[1])\n" +
//                        "else\n" +
//                        "    return 0\n" +
//                        "end";
//                    redisScript.setScriptText(text);
//                    redisScript.setResultType(Long.class);
//                    redisTemplate.execute(redisScript, Arrays.asList(lockKey), lockValue);
//                }
//
//            }else{
//            //2.3 获得锁失败，自旋
//                Thread.sleep(200);
//                return this.getAlbumInfo(id);
//            }
//        } catch (Exception e) {
//           log.error("[专辑服务]查询专辑信息异常：{}", e);
//            //兜底处理，查询数据库返回业务数据
//            return this.getAlbumInfoFromDB(id);
//        }
//
//    }
    @Override
    public AlbumInfo getAlbumInfo(Long id) {
        try {
            String dataKey = RedisConstant.ALBUM_INFO_PREFIX + id;
            String lockKey = RedisConstant.ALBUM_LOCK_SUFFIX + id;
            //1.查询缓存
            AlbumInfo albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(dataKey);
            if (albumInfo != null) {
                return albumInfo;
            }
            //2.没有缓存,开启分布式锁
            RLock lock = redissonClient.getLock(lockKey);
            //2.1开启锁
            lock.lock();
            //2.2重新查询缓存,如果有数据直接返回
            try {
                albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(dataKey);
                if (albumInfo != null) {
                    return albumInfo;
                }
                //3.执行业务 没有缓存，查询数据库
                albumInfo = this.getAlbumInfoFromDB(id);
                //3.1 如果没有数据,设置较短缓存过期时间，返回空数据
                if (albumInfo == null) {
                    redisTemplate.opsForValue().set(dataKey, null, RedisConstant.ALBUM_TEMPORARY_TIMEOUT, TimeUnit.SECONDS);
                    return albumInfo;
                }
                //3.2 如果有数据，设置缓存过期时间，返回数据
                long ttl = RandomUtil.randomLong(500, 3600);
                redisTemplate.opsForValue().set(dataKey, albumInfo, RedisConstant.ALBUM_TIMEOUT + ttl, TimeUnit.SECONDS);
                return albumInfo;
            } finally {
                //4.释放锁
                lock.unlock();
            }
        } catch (Exception e) {
            log.error("[专辑服务]查询专辑信息异常：{}", e);
            //保底处理，查询数据库返回业务数据
            return this.getAlbumInfoFromDB(id);
        }
    }

    /**
     * 根据ID查询专辑信息
     *
     * @param id
     * @return
     */
    @GuiGuCache(prefix = "album:info")
    @Override
    public AlbumInfo getAlbumInfoFromDB(Long id) {
        //方式一 查询专辑信息 再查询属性关联表进行封装
//        AlbumInfo albumInfo = this.getById(id);
//        List<AlbumAttributeValue> attributeValueList = albumAttributeValueMapper.selectList(new LambdaQueryWrapper<AlbumAttributeValue>()
//                .eq(AlbumAttributeValue::getAlbumId, id));
//        albumInfo.setAlbumAttributeValueVoList(attributeValueList);

        //方式二 自定义sql多表查询一对多封装
        AlbumInfo albumInfo = albumInfoMapper.getAlbumInfo(id);
        return albumInfo;
    }

    /**
     * 专辑修改
     * 1.修改专辑信息
     * 2.修改专辑属性值（先删除旧的属性，再新增）
     *
     * @param id          专辑ID
     * @param albumInfoVo 修改后专辑信息
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAlbumInfo(Long id, AlbumInfoVo albumInfoVo) {
        //1.修改专辑信息
        //1.1 将专辑VO转为PO对象
        AlbumInfo albumInfo = BeanUtil.copyProperties(albumInfoVo, AlbumInfo.class);
        albumInfo.setId(id);
        //1.2 执行修改专辑信息
        albumInfoMapper.updateById(albumInfo);

        //2.修改专辑属性信息
        //2.1 先根据专辑ID条件删除专辑属性关系(逻辑删除)
        LambdaQueryWrapper<AlbumAttributeValue> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AlbumAttributeValue::getAlbumId, id);
        albumAttributeValueMapper.delete(queryWrapper);
        //2.2 再根据用户提交专辑属性新增（关联专辑ID）
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        if (CollectionUtil.isNotEmpty(albumAttributeValueVoList)) {
            albumAttributeValueVoList.forEach(albumAttributeValueVo -> {
                //转为PO对象 关联专辑ID
                AlbumAttributeValue albumAttributeValue = BeanUtil.copyProperties(albumAttributeValueVo, AlbumAttributeValue.class);
                albumAttributeValue.setAlbumId(id);
                albumAttributeValueMapper.insert(albumAttributeValue);
            });
        }
        //TODO如果是开放专辑自动将专辑同步到ES索引库中
        if ("1".equals(albumInfo.getIsOpen())) {
            kafkaService.sendMessage(KafkaConstant.QUEUE_ALBUM_UPPER, albumInfo.getId().toString());
        } else {
            kafkaService.sendMessage(KafkaConstant.QUEUE_ALBUM_LOWER, albumInfo.getId().toString());
        }
    }

    /**
     * 获取当前用户全部专辑列表
     *
     * @param userId
     * @return
     */
    @Override
    public List<AlbumInfo> findUserAllAlbumList(Long userId) {
        return albumInfoMapper.selectList(new LambdaQueryWrapper<AlbumInfo>()
                .eq(AlbumInfo::getUserId, userId)
                .select(AlbumInfo::getId, AlbumInfo::getAlbumTitle, AlbumInfo::getCreateTime)
                .orderByDesc(AlbumInfo::getCreateTime)
                .last("limit 200"));
    }

    /**
     * 根据专辑ID获取专辑统计信息
     *
     * @param albumId
     * @return
     */
    @GuiGuCache(prefix = "albumstat")
    @Override
    public AlbumStatVo getAlbumStatVo(Long albumId) {
        return albumInfoMapper.getAlbumStatVo(albumId);
    }
}
